// make this another standalone module
import { getNameFromPayload } from 'jsonql-params-validator'
import { LOGIN_EVENT_NAME, LOGOUT_EVENT_NAME, JS_WS_NAME } from 'jsonql-constants'
import { nspClient, nspAuthClient } from '../utils/create-nsp-client'
// this become the cb
import clientEventHandler from '../client-event-handler'
import triggerNamespacesOnError from '../utils/trigger-namespaces-on-error'
// move this up one level from client-event-handler
import wsMainHandler from './ws-main-handler'
import { getDebug, clearMainEmitEvt, getNamespaceInOrder, disconnect } from '../utils'
const debugFn = getDebug('ws-create-client')

/**
 * Because the nsps can be throw away so it doesn't matter the scope
 * this will get reuse again
 * @param {object} opts configuration
 * @param {object} nspMap from contract
 * @param {string|null} token whether we have the token at run time
 * @return {object} nsps namespace with namespace as key
 */
const createNsps = function(opts, nspMap, token) {
  let { nspSet, publicNamespace } = nspMap;
  let login = false;
  let namespaces = [];
  let nsps = {};
  // first we need to binding all the events handler
  if (opts.enableAuth && opts.useJwt) {
    login = true; // just saying we need to listen to login event
    namespaces = getNamespaceInOrder(nspSet, publicNamespace)
    nsps = namespaces.map((namespace, i) => {
      if (i === 0) {
        if (token) {
          opts.token = token;
          return {[namespace]: nspAuthClient(namespace, opts)}
        }
        return {[namespace]: false}
      }
      return {[namespace]: nspClient(namespace, opts)}
    }).reduce((first, next) => Object.assign(first, next), {})
  } else {
    let namespace = getNameFromPayload(nspSet)
    namespaces.push(namespace)
    // standard without login
    // the stock version should not have a namespace
    nsps[namespace] = nspClient(false, opts)
  }
  // return
  return { nsps, namespaces, login }
}

/**
 * create a ws client
 * @param {object} opts configuration
 * @param {object} nspMap namespace with resolvers
 * @param {object} ee EventEmitter to pass through
 * @return {object} what comes in what goes out
 */
export default function createClient(opts, nspMap, ee) {
  // arguments that don't change
  let args = [opts, nspMap, ee, wsMainHandler]
  // now create the nsps
  let { nsps, namespaces, login } = createNsps(opts, nspMap, opts.token)
  // binding the listeners - and it will listen to LOGOUT event
  // to unbind itself, and the above call will bind it again
  Reflect.apply(clientEventHandler, null, args.concat([namespaces, nsps]))
  // setup listener
  if (login) {
    ee.$only(LOGIN_EVENT_NAME, function(token) {
      disconnect(nsps, JS_WS_NAME)
      // @TODO should we trigger error on this one?
      // triggerNamespacesOnError(ee, namespaces, LOGIN_EVENT_NAME)
      clearMainEmitEvt(ee, namespaces)
      debugFn('LOGIN_EVENT_NAME', token)
      let newNsps = createNsps(opts, nspMap, token)
      // rebind it
      Reflect.apply(
        clientEventHandler,
        null,
        args.concat([newNsps.namespaces, newNsps.nsps])
      )
    })
  }
  // return what input
  return { opts, nspMap, ee }
}
